﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using LaughingDog.GameObjects;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using LaughingDog.Components.Shapes;

namespace LaughingDog.Components
{
    public class Shape : Component, IDrawableComponent
    {
        private Texture2D mPixel;
        private List<Vector2> mVectors;
        private Color mColour = Color.White;
        private Single mLayerDepth = 1;
        private Single mLineThickness = 1;

        private Boolean mInCameraView = true;
        private Boolean mVisible = true;

        private Vector2 mPositionOffset = Vector2.Zero;
        private Single mRotationOffset = 0.0f;        

        /// <summary>
        /// Gets the list of Vector2s that have been used in this Shape.
        /// </summary>
        public List<Vector2> Vectors { get { return mVectors; } }

        /// <summary>
        /// Gets or sets the colour that should be used when drawing this Shape. Default is White.
        /// </summary>
        public Color Colour { get { return mColour; } set { mColour = value; } }

        /// <summary>
        /// The layer depth of this Renderer from 1 (highest) to 0 (lowest).
        /// </summary>
        public Single LayerDepth { get { return mLayerDepth; } set { mLayerDepth = value; } }

        /// <summary>
        /// Gets or sets whether this Component is in the view of the Camera. Should not be used manually.
        /// </summary>
        public Boolean InCameraView { get { return mInCameraView; } set { mInCameraView = value; } }

        /// <summary>
        /// Gets or sets whether this Shape should be hidden even if it is in view of the camera.
        /// </summary>
        public Boolean Visible { get { return mVisible; } set { mVisible = value; } }

        /// <summary>
        /// The thickness of the Shapes line.
        /// </summary>
        public Single LineThickness { get { return mLineThickness; } set { mLineThickness = value; } }

        /// <summary>
        /// Gets or sets a value indicating the position this Shape should be offset from its GameObjects position.
        /// </summary>
        public Vector2 PositionOffset { get { return mPositionOffset; } set { mPositionOffset = value; } }

        /// <summary>
        /// Gets or sets a value indicating the rotation (in degrees) this Shape should be offset from its GameObjects rotation.
        /// </summary>
        public Single RotationOffset { get { return MathHelper.ToDegrees(mRotationOffset); } set { mRotationOffset = MathHelper.ToRadians(value); } }

        /// <summary>
        /// Gets or sets a value indicating the rotation this Shape should be offset from its GameObjects rotation.
        /// </summary>
        public Single RotationOffsetRadians { get { return mRotationOffset; } set { mRotationOffset = value; } }        

        /// <summary>
        /// Creates a new instance of a Shape component.
        /// </summary>
        /// <param name="gameObject">The GameObject to attach this Component to.</param>
        public Shape(GameObject gameObject)
            : base(gameObject)
        {
            // create pixels
            mPixel = new Texture2D(Engine.GraphicsDeviceManager.GraphicsDevice, 1, 1, 1, TextureUsage.None, SurfaceFormat.Color);
            Color[] pixels = new Color[1];
            pixels[0] = Color.White;
            mPixel.SetData<Color>(pixels);

            mVectors = new List<Vector2>();            
        }

        /// <summary>
        /// Adds a Vector2 to the shape.
        /// </summary>
        /// <param name="vector">The Vector2 to add.</param>
        public void AddVector(Vector2 vector)
        {
            mVectors.Add(vector);
        }

        /// <summary>
        /// Interts a Vector2 at the specified index.
        /// </summary>
        /// <param name="index">The index at which the vector should be instered.</param>
        /// <param name="vector">The Vector2 to insert.</param>
        public void InsertVector(Int32 index, Vector2 vector)
        {
            mVectors.Insert(index, vector);
        }

        /// <summary>
        /// Removes the specified Vector2 from the shape.
        /// </summary>
        /// <param name="vector">The Vector2 to remove.</param>
        public void RemoveVector(Vector2 vector)
        {
            mVectors.Remove(vector);
        }

        /// <summary>
        /// Removes the Vector2 at the specified index.
        /// </summary>
        /// <param name="index">The index of the Vector2 to remove.</param>
        public void RemoveVector(Int32 index)
        {
            mVectors.RemoveAt(index);
        }

        /// <summary>
        /// Removes all the Vector2s from the shape.
        /// </summary>
        public void ClearVectors()
        {
            mVectors.Clear();
        }

        /// <summary>
        /// Draws this Shape component.
        /// </summary>
        /// <param name="spriteBatch">The SpriteBatch to use for drawing.</param>
        public void Draw(SpriteBatch spriteBatch)
        {
            if (mPixel == null)
                return;

            if (mVectors.Count < 2)
                return;

            for (int i = 1; i < mVectors.Count; i++)
            {
                Vector2 vector1 = mVectors[i - 1];
                Vector2 vector2 = mVectors[i];

                // calculate the distance between the two vectors
                Single distance = Vector2.Distance(vector1, vector2);

                // calculate the angle between the two vectors
                Single angle = (Single)Math.Atan2((Double)(vector2.Y - vector1.Y),
                    (Double)(vector2.X - vector1.X));

                Vector2 origin = new Vector2(0.5f, 0.5f);

                Vector2 position = (Transform.Position + mPositionOffset) + vector1 + 0.5f * (vector2 - vector1);

                //if (GetType() == typeof(PolygonShape))
                //    position -= Renderer.Origin;

                // stretch the pixel between the two vectors
                spriteBatch.Draw(mPixel,
                    position,
                    null,
                    mColour,
                    angle,
                    origin, 
                    new Vector2(distance, mLineThickness),
                    SpriteEffects.None,
                    mLayerDepth);
            }
        }

        /// <summary>
        /// Updates this Shape component.
        /// </summary>
        public override void Update()
        {            
            // rotate it as necessary
            Matrix matrix = Matrix.CreateRotationZ(Transform.Rotation + mRotationOffset);
            for (int i = 0; i < Vectors.Count; i++)
            {
                Vectors[i] = Vector2.Transform(Vectors[i], matrix);
            }

            base.Update();
        }
    }
}
